package com.zhh.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import com.zhh.nio.NioServer.Poller;

public class NioSession {
    
    private SocketChannel sc;
    private ByteBuffer  readBuffer;
    private ByteBuffer writeBuffer;
    private SessionHandler sessionHandler;
    
    private Queue<WriteRequest> writeQ;
    
    private int readBufferSize = 8092;
    private int writeBufferSize = 8092;
    //belong to which poller
    private Poller poller;
    
    public NioSession(SocketChannel sc){
        this.sc = sc;
        readBuffer = ByteBuffer.allocate(readBufferSize);
        writeBuffer = ByteBuffer.allocate(writeBufferSize);
        writeQ = new LinkedBlockingQueue<WriteRequest>(32);
    }
    
    public SessionHandler getSessionHandler() {
        return sessionHandler;
    }
    public void setSessionHandler(SessionHandler sessionHandler) {
        this.sessionHandler = sessionHandler;
    }

    public Poller getPoller() {
        return poller;
    }

    public void setPoller(Poller poller) {
        this.poller = poller;
    }
    
    public void register() throws IOException {
        
        SelectionKey key =  sc.register(getPoller().getSelector(), 0);
        System.out.println("register successfully");
        key.interestOps(SelectionKey.OP_READ);
        //将session放在selectionKey中
        sc.keyFor(getPoller().getSelector()).attach(this);
    } 
    
    public void write(Object message){
        //enableWrite();
        writeQ.offer(wrapWriteRequest(message));
        getPoller().getSelector().wakeup();
    }
    
    public void read() {
        
        readBuffer.clear();
        int readNum = -1;
        int oneTimeRead = 0;
        try {
//            while((oneTimeRead = sc.read(readBuffer)) > 0){
//                readNum += oneTimeRead;
//            }
            readNum = sc.read(readBuffer);
        } catch (IOException e) {
           this.close();
        }
        
        if(readNum != -1){
            readBuffer.flip();
            byte[] copy = new byte[readNum];
            System.arraycopy(readBuffer.array(), 0, copy, 0, readNum);
            sessionHandler.messageReceived(this, copy);
            enableWrite();
        }else{
        	System.out.println("session close from session read");
            this.close();
        }
        
    }
    
    protected void write(){
        while(!writeQ.isEmpty()){
            
            WriteRequest head = writeQ.poll();
            if(head != null){
                try {
                    System.out.println("cappcity:" + head.getWriteBuffer().capacity());
                    int numSend = sc.write(head.getWriteBuffer());
                    System.out.println("send:" + numSend);
                } catch (IOException e) {
                   e.printStackTrace();
                   System.out.println("session close from session write");
                   this.close();
                }
            }
            enableRead();
        }
    }
    
    public void enableWrite(){
    	SelectionKey key = sc.keyFor(getPoller().getSelector());
    	if(key.isValid()){
    		key.interestOps(SelectionKey.OP_WRITE);
    	}
    }
    
    public void enableRead(){
    	SelectionKey key = sc.keyFor(getPoller().getSelector());
    	if(key.isValid()){
    		key.interestOps(SelectionKey.OP_READ);
    	}
    }
    
    private WriteRequest wrapWriteRequest(Object message){
        ByteBuffer byteBuffer = null;
       
        if(message instanceof byte[]){
            byteBuffer = ByteBuffer.wrap((byte[])message);
        }else{
            String msg = (String)message;//TODO 添加encoding和decoding的策略模式
            byte[] orginal = msg.getBytes();
            //byte[] send = new byte[orginal.length + 1];
            //System.arraycopy(orginal, 0, send, 0, orginal.length);
            //send[orginal.length] = -1;
            byteBuffer = ByteBuffer.wrap(orginal);
        }
       
        WriteRequest writeRequest = new WriteRequest();
        writeRequest.setWriteBuffer(byteBuffer);
        return writeRequest;
    }
    
    public void close(){
        try {
        	System.out.println("Session: closed" + NioSession.this);
            sc.keyFor(getPoller().getSelector()).cancel();
            this.sc.close();
            
        } catch (IOException e) {
            // TODO
            e.printStackTrace();
        }
    }

	@Override
	public String toString() {
		return "NioSession [sc=" + sc + ", poller=" + poller + "]";
	}

	
    
    
}
